﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Tracy.DataModel;
using SharedLib.BasicServices.Plugin.Configuration;
using System.Xml;
using SharedLib.Helpers;
using System.Text.RegularExpressions;
using System.IO;
using SharedLib.BasicServices;
using Tracy;
using Tracy.DataServices;
using Tracy.Messages;
using SharedLib.BasicServices.MessageFramework;

namespace TracyGUI
{
    public partial class FormEntry : Form //TODO: progress bar
    {
        const string INITIAL_SEARCH_TEXT = "Enter Search Keywords Here";
        public enum EntryEditMode { Create, Modify };

        Entry _editingEntry;
        List<FilterSet> _displayedFilterSets = new List<FilterSet>();
        List<FilterSet> _selectedFilterSets = new List<FilterSet>();
        FormInProgress _formInProgress;
        

        bool _hasChange = false;
        bool _isUIRefreshing = false;
        
        private bool HasChange
        {
            get { return _hasChange; }
            set { _hasChange = value; }
        }

        public Entry EditingEntry
        {
            get { return _editingEntry; }
            private set 
            { 
                _editingEntry = value;
                
            }
        }
        private List<FilterSet> DisplayedFilterSets
        {
            get { return _displayedFilterSets; }
            set { _displayedFilterSets = value; }
        }
        public List<FilterSet> SelectedFilterSets
        {
            get { return _selectedFilterSets; }
            private set { _selectedFilterSets = value; }
        }

        EntryEditMode _editingMode = EntryEditMode.Create;

        public FormEntry(EntryEditMode mode, Entry entry)
        {
            InitializeComponent();

            _editingMode = mode;
            EditingEntry = entry;
            DisplayedFilterSets.Clear();
            if (_editingMode == EntryEditMode.Create)
            {
                HasChange = true;
            }
            else
            {
                foreach (FilterSet filterSet in TracyBus.Instance.FilterProvider.GetFilterSets(entry))
                {
                    DisplayedFilterSets.Add(filterSet);
                    SelectedFilterSets.Add(filterSet);
                }
            }
        }
        
        private void FormEntry_Load(object sender, EventArgs e)
        {

            InitiateUI();

            if (_editingMode == EntryEditMode.Create)
            {
                textBoxKeywords.SelectAll();
            }

        }

        private void InitiateUI()
        {
            _isUIRefreshing = true;

            if (EditingEntry != null)
            {
                textBoxName.Text = EditingEntry.Name;
                textBoxRomaji.Text = EditingEntry.Romaji;
                textBoxKeywords.Text = EditingEntry.SearchKeywords;
                textBoxPath.Text = EditingEntry.SavePath;
                textBoxRegex.Text = EditingEntry.RegExpr;

                RefreshSearchResult(null);
            }
            else
            {
                textBoxName.Text = "";
                textBoxRomaji.Text = "";
                textBoxKeywords.Text = INITIAL_SEARCH_TEXT;
                textBoxPath.Text = "";
                textBoxRegex.Text = "";

                listViewSearchResult.Items.Clear();
            }

            _isUIRefreshing = false;
        }

        static string PreProcessTitle(string title)//TODO: move to sharedlib
        {
            return Microsoft.VisualBasic.Strings.StrConv(title.Replace("【", "[").Replace("】", "]").Replace("×", "X").ToUpper(), Microsoft.VisualBasic.VbStrConv.SimplifiedChinese);
        }

        



        private void buttonSearch_Click(object sender, EventArgs e)
        {
            PerformSearch();
        }

        private void PerformSearch()
        {

            if (String.IsNullOrEmpty(textBoxKeywords.Text) || textBoxKeywords.Text == INITIAL_SEARCH_TEXT)
            {
                MessageBox.Show("Please enter keywords");
                return;
            }

            if (_editingMode == EntryEditMode.Create)
            {
                EditingEntry = new Entry(textBoxKeywords.Text);
                textBoxName.Text = EditingEntry.Name;
                textBoxRomaji.Text = EditingEntry.Romaji;
                textBoxKeywords.Text = EditingEntry.SearchKeywords;
                textBoxPath.Text = EditingEntry.SavePath;
                textBoxRegex.Text = EditingEntry.RegExpr;
            }
            else
            {
                EditingEntry.SearchKeywords = textBoxKeywords.Text;
                //TODO: advanced mode for regExpr
                //string regExpr = Entry.GenerateRegExpr(textBoxKeywords.Text);
                //textBoxRegex.Text = regExpr;
                //EditingEntry.RegExpr = regExpr;
            }

            FeedSource source = TracyBus.Instance.ConfigurationManager.FeedSourceProvider.GetItems[0];
            //TODO: Support more sources
            
            string rssURL = source.GenerateUrl(EditingEntry);
            TracyBus.Instance.MessageDispatcher.EnqueueMessage(new GetXmlFromFeedSourceMessage(this, source, EditingEntry, new SharedLib.BasicServices.MessageFramework.MessageCallback(OnRSSRetrieved)));
            _formInProgress = new FormInProgress();
            _formInProgress.ShowDialog();
        }

        private void OnRSSRetrieved(IAppMessage msg)
        {
            if (msg != null && msg is GetXmlFromFeedSourceMessage)
            {
                //Validate if user cancels the task
                if (_formInProgress == null) return;
                _formInProgress.Hide();
                if (_formInProgress.DialogResult == System.Windows.Forms.DialogResult.Cancel) return;

                if (msg.Exception == null)
                {
                    //Clear previous result
                    DisplayedFilterSets.Clear();
                    foreach (FilterSet fs in SelectedFilterSets) DisplayedFilterSets.Add(fs);

                    XmlDocument rawDoc = msg.Result as XmlDocument;
                    FeedSource source = ((GetXmlFromFeedSourceMessage)msg).Source;
                    ISourceParser parser = TracyBus.Instance.RegisteredObjectService.CreateInstance<ISourceParser>(source.Parser);
                    XmlDocument doc = parser.GetRawResources(source, rawDoc);

                    Dictionary<FilterSet, List<string>> matchedResources = new Dictionary<FilterSet, List<string>>();

                    Regex regEx = new Regex(EditingEntry.RegExpr.ToUpper());

                    foreach (XmlElement resourceNode in doc.SelectNodes("result/resource"))
                    {
                        string title = XmlHelper.GetNodeValue(resourceNode, "title");
                        string titleToFilter = PreProcessTitle(title);

                        if (regEx.Match(titleToFilter).Success)//match entry
                        {
                            //Parse FanSubGroup
                            List<FanSubGroup> resourceFanSubGroups = FilterProvider.GetFanSubGroups(titleToFilter);
                            if (resourceFanSubGroups.Count > 0)
                            {
                                //Parse Format
                                Format resourceFormat = FilterProvider.GetFormat(titleToFilter);
                                if (resourceFormat != null)
                                {
                                    //Parse Resolution
                                    Resolution resourceResolution = FilterProvider.GetResolution(titleToFilter);
                                    if (resourceResolution != null)
                                    {
                                        //Build FilterSet
                                        FilterSet resourceFilterSet = new FilterSet();
                                        foreach (FanSubGroup fanSubGroup in resourceFanSubGroups)
                                            resourceFilterSet.FanSubGroups.Add(fanSubGroup);

                                        resourceFilterSet.Format = resourceFormat;
                                        resourceFilterSet.Resolution = resourceResolution;

                                        int index = -1;
                                        for (int i = 0; i < DisplayedFilterSets.Count; i++)
                                        {
                                            if (DisplayedFilterSets[i].Equals(resourceFilterSet))
                                            {
                                                index = i;
                                                break;
                                            }
                                        }

                                        if (index >= 0)
                                        {
                                            //Map to Existing FilterSet
                                            FilterSet existingFilterSet = DisplayedFilterSets[index];
                                            if (matchedResources.ContainsKey(existingFilterSet))
                                            {
                                                //Already cached
                                                matchedResources[existingFilterSet].Add(titleToFilter);
                                            }
                                            else
                                            {
                                                //Not cached yet
                                                AppendFilterSetToMatchedResources(matchedResources, titleToFilter, existingFilterSet);
                                            }
                                        }
                                        else
                                        {
                                            //New FilterSet found
                                            DisplayedFilterSets.Add(resourceFilterSet);
                                            AppendFilterSetToMatchedResources(matchedResources, titleToFilter, resourceFilterSet);
                                        }
                                    }
                                }
                            }
                        }
                    }

                    RefreshSearchResult(matchedResources);
                }
                else
                {
                    MessageBox.Show("Failed to retrieve RSS feed: " + msg.Exception.Message);
                }
            }
        }

        private static void AppendFilterSetToMatchedResources(Dictionary<FilterSet, List<string>> matchedResources, string titleToFilter, FilterSet resourceFilterSet)
        {
            List<string> matchedTitles = new List<string>();
            matchedTitles.Add(titleToFilter);
            matchedResources.Add(resourceFilterSet, matchedTitles);
        }

        private void RefreshSearchResult(Dictionary<FilterSet, List<string>> matchedResources)
        {
            listViewSearchResult.Items.Clear();

            for (int i = 0; i < DisplayedFilterSets.Count; i++)
            {
                FilterSet currentFilterSet = DisplayedFilterSets[i];

                ListViewItem lvItemFilterSet = new ListViewItem();
                lvItemFilterSet.Name = "Object";
                lvItemFilterSet.Tag = currentFilterSet;
                if (SelectedFilterSets.Contains(currentFilterSet)) lvItemFilterSet.Checked = true;

                //TODO: Refact code
                string count = "N/A";
                if(matchedResources != null && matchedResources.ContainsKey(currentFilterSet)) count = matchedResources[currentFilterSet].Count.ToString();
                ListViewItem.ListViewSubItem lvItemCount = new ListViewItem.ListViewSubItem(lvItemFilterSet, count);
                lvItemCount.Name = "Count";
                lvItemFilterSet.SubItems.Add(lvItemCount);

                ListViewItem.ListViewSubItem lvItemFanSubGroup = new ListViewItem.ListViewSubItem(lvItemFilterSet, FanSubGroup.GetCombinationName(DisplayedFilterSets[i].FanSubGroups));
                lvItemFanSubGroup.Name = "FanSubGroup";
                lvItemFilterSet.SubItems.Add(lvItemFanSubGroup);

                ListViewItem.ListViewSubItem lvItemResolution = new ListViewItem.ListViewSubItem(lvItemFilterSet, currentFilterSet.Resolution.Name);
                lvItemResolution.Name = "Resolution";
                lvItemFilterSet.SubItems.Add(lvItemResolution);

                ListViewItem.ListViewSubItem lvItemFormat = new ListViewItem.ListViewSubItem(lvItemFilterSet, currentFilterSet.Format.Name);
                lvItemFormat.Name = "Format";
                lvItemFilterSet.SubItems.Add(lvItemFormat);

                string example = "N/A";
                if (matchedResources != null && matchedResources.ContainsKey(currentFilterSet)) example = matchedResources[currentFilterSet][0];
                ListViewItem.ListViewSubItem lvItemExample = new ListViewItem.ListViewSubItem(lvItemFilterSet, example);
                lvItemExample.Name = "Example";
                lvItemFilterSet.SubItems.Add(lvItemExample);

                listViewSearchResult.Items.Add(lvItemFilterSet);
            }
        }

        private void textBoxKeywords_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                PerformSearch();
            }
        }

        private void buttonDone_Click(object sender, EventArgs e)
        {
            UpdateEntry();
            HasChange = false;
            DialogResult = System.Windows.Forms.DialogResult.OK;
            Close();
        }

        /// <summary>
        /// Update _editingEntry, which will be referenced by FormMain
        /// </summary>
        private void UpdateEntry()
        {
            if (EditingEntry == null)
            {
                if (_editingMode == EntryEditMode.Create)
                    EditingEntry = new Entry();
                else
                    throw new Exception("Unexcepted Exception: Editig Entry is null");
            }

            EditingEntry.Name = textBoxName.Text;
            EditingEntry.Romaji = textBoxRomaji.Text;
            EditingEntry.SearchKeywords = textBoxKeywords.Text;
            EditingEntry.SavePath = textBoxPath.Text;
            EditingEntry.RegExpr = textBoxRegex.Text;

        }



        private void FormEntry_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (HasChange)
            {
                System.Windows.Forms.DialogResult result = MessageBox.Show("Changes not saved, close window?", "Exiting", MessageBoxButtons.OKCancel);
                if (result == System.Windows.Forms.DialogResult.OK)
                {
                    DialogResult = System.Windows.Forms.DialogResult.Cancel;
                }
                else
                {
                    e.Cancel = true;
                }
            }
        }

        private void SetChangeFlag()
        {
            if (!_isUIRefreshing)
            {
                HasChange = true;
                buttonDone.Enabled = true;
            }
        }

        private void textBoxKeywords_TextChanged(object sender, EventArgs e)
        {
            if (_editingEntry != null)
            {
                SetChangeFlag();
            }
        }

        private void textBoxName_TextChanged(object sender, EventArgs e)
        {
            SetChangeFlag();
        }

        private void textBoxRomaji_TextChanged(object sender, EventArgs e)
        {
            SetChangeFlag();
        }

        private void textBoxPath_TextChanged(object sender, EventArgs e)
        {
            SetChangeFlag();
        }

        private void textBoxRegex_TextChanged(object sender, EventArgs e)
        {
            SetChangeFlag();//TODO: validate expression
        }

        private void listViewSearchResult_ItemChecked(object sender, ItemCheckedEventArgs e)
        {
            SetChangeFlag();
            FilterSet filterSet = e.Item.Tag as FilterSet;
            if (filterSet != null)
            {
                if (e.Item.Checked)
                {

                    if (!SelectedFilterSets.Contains(filterSet))
                    {
                        SelectedFilterSets.Add(filterSet);
                    }
                }
                else
                {
                    if (SelectedFilterSets.Contains(filterSet))
                    {
                        SelectedFilterSets.Remove(filterSet);
                    }
                }
            }
        }

        private void buttonBrowsePath_Click(object sender, EventArgs e)
        {
            if(Directory.Exists(textBoxPath.Text)) folderBrowserDialogEntryFolder.SelectedPath = textBoxPath.Text;
            if (folderBrowserDialogEntryFolder.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                textBoxPath.Text = folderBrowserDialogEntryFolder.SelectedPath;
            }
        }

        private void textBoxKeywords_MouseClick(object sender, MouseEventArgs e)
        {
            if (textBoxKeywords.Text == INITIAL_SEARCH_TEXT)
            {
                textBoxKeywords.SelectAll();
            }
        }
    }
}
